package com.qfmy.service.impl.assistant;

import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.qfmy.config.DashScopeConfig;
import com.qfmy.mapper.assistant.InterviewMapper;
import com.qfmy.service.assistant.InterviewService;
import common.exception.BaseException;
import common.login.LoginUser;
import common.login.LoginUserHolder;
import common.result.ResultCodeEnum;
import io.reactivex.Flowable;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import model.entity.assistant.Interview;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.FluxSink;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author 清风明月
 * @Package com.qfmy.service.impl.assistant
 * @date 2025/5/19
 * @description 面试
 */
@Slf4j
@Service
@SuppressWarnings("all")
public class InterviewServiceImpl implements InterviewService {

    /**
     * 注入mapper
     */
    @Autowired
    private InterviewMapper interviewMapper;
    /**
     * 注入redisTemplate
     */
    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 注入dashiscope配置
     */
    @Autowired
    private DashScopeConfig dashScopeConfig;

    /**
     * 生成器
     */
    private Generation generation;

    // 初始化生成器实例
    @PostConstruct
    public void init() {
        generation = new Generation();
    }


    /**
     * 设置redis的key
     */
    private static final String CHAT_HISTORY_KEY_PREFIX = "user:";

    /**
     * 保存面试
     * @param interview
     */
    @Override
    public void saveResume(Interview interview) {
        //获取用户id
        Long userId = getUserId();
        //根据用户id获取简历
        Interview resume = interviewMapper.getResume(userId);
        //判断是否存在
        if (resume != null) {
            //存在则更新
            interviewMapper.updateResume(interview);
        }else{
            resume = new Interview();
            resume.setUserId(userId);
            resume.setContent(interview.getContent());
            resume.setPosition(interview.getPosition());
            resume.setDes(interview.getDes());
            //不存在则插入
            interviewMapper.saveResume(resume);
        }

    }

    /**
     * 判断是否上传了简历
     * @param interview
     * @return
     */
    @Override
    public Boolean getResume() {
        //获取用户id
        Long userId = getUserId();
        //根据用户id获取简历
        Interview resume = interviewMapper.getResume(userId);
        //判断是否存在
        if (resume != null) {
            return true;
        }
        return false;
    }

    /**
     * 面试
     * @param prompt
     * @param isOne
     * @return
     */
    @Override
    public Flux<String> interview(String question, Boolean isOne) {
        //获取当前用户id
        Long userId = getUserId();
        //获取历史记录
        var ref = new Object() {
            List<Message> history = getRedisChatHistory(userId);
        };
        //判断是否为空
        if(ref.history ==null)
        {
            ref.history = new ArrayList<>();
        } else {
            ref.history = getRedisChatHistory(userId);
        }
        //判断isOne是否为真
        if(isOne)
        {
            //获取简历
            Interview interview = interviewMapper.getResume(userId);
            //获取岗位
            String position = interview.getPosition();
            //获取简历内容
            String resume = interview.getContent();
            //构建问题
           String prompt = "你是一个面试官，现在需要你为" + position + "岗位准备面试问题，并且根据" + resume + "回答面试问题，并且不要超过" + position + "岗位的职位要求," +
                    "你可以问这个简历的技术栈或者做的项目相关的知识,也可以问" + position + "岗位的相关知识注意:你作为面试官你一次只问一个问题让用户回答你问的问题后在问下一个问题";
            // 添加历史记录
            Message message = new Message();
            message.setRole(Role.ASSISTANT.getValue());
            message.setContent(prompt);
            ref.history.add(message);
        }else {
            // 添加历史记录
            Message message = new Message();
            message.setRole(Role.USER.getValue());
            message.setContent(question);
            ref.history.add(message);
        }

        return Flux.create(emitter -> {
            StringBuilder aiResponseBuilder = new StringBuilder(); // 用于构建AI的完整回复

            try {
                // 1. 构建请求参数
                GenerationParam param = GenerationParam.builder()
                        .model(dashScopeConfig.getModelText())
                        .apiKey(dashScopeConfig.getApiKey())
                        .messages(ref.history)
                        .topP(0.8)
                        .resultFormat(GenerationParam.ResultFormat.MESSAGE)
                        .incrementalOutput(true) // 开启增量输出
                        .enableSearch(true)
                        .build();

                // 2. 调用流式API
                Flowable<GenerationResult> flowable = generation.streamCall(param);

                // 3. 将Flowable转换为Flux
                flowable.subscribe(
                        result -> processResult(result, emitter, aiResponseBuilder),
                        emitter::error,
                        () -> {
                            emitter.complete();
                            // 将AI的消息添加到历史记录
                            Message aiMessage = new Message();
                            aiMessage.setRole(Role.ASSISTANT.getValue());
                            aiMessage.setContent(aiResponseBuilder.toString());
                            ref.history.add(aiMessage);
                            String key = CHAT_HISTORY_KEY_PREFIX + userId + ":chatHistory";
                            // 将历史记录保存到Redis，设置过期时间为1个小时
                            redisTemplate.opsForValue().set(key, ref.history, 2, TimeUnit.HOURS);
                        }
                );

            } catch (ApiException | InputRequiredException | NoApiKeyException e) {
                emitter.error(e);
            }
        });
    }

    /**
     * 面试语音
     * @param prompt
     * @param isOne
     * @return
     */
    @Override
    public String interviewVoice(String question, Boolean isOne) {
        //获取当前用户id
        Long userId = getUserId();
        //获取历史记录
        var ref = new Object() {
            List<Message> history = getRedisChatHistory(userId);
        };
        //判断是否为空
        if(ref.history ==null)
        {
            ref.history = new ArrayList<>();
        } else {
            ref.history = getRedisChatHistory(userId);
        }
        //判断isOne是否为真
        if(isOne)
        {
            //获取简历
            Interview interview = interviewMapper.getResume(userId);
            //获取岗位
            String position = interview.getPosition();
            //获取简历内容
            String resume = interview.getContent();
            //构建问题
            String prompt = "你是一个面试官，现在需要你为" + position + "岗位准备面试问题，并且根据" + resume + "回答面试问题，并且不要超过" + position + "岗位的职位要求," +
                    "你可以问这个简历的技术栈或者做的项目相关的知识,也可以问" + position + "岗位的相关知识注意:你作为面试官你一次只问一个问题让用户回答你问的问题后在问下一个问题";
            // 添加历史记录
            Message message = new Message();
            message.setRole(Role.ASSISTANT.getValue());
            message.setContent(prompt);
            ref.history.add(message);
        }else {
            // 添加历史记录
            Message message = new Message();
            message.setRole(Role.USER.getValue());
            message.setContent(question);
            ref.history.add(message);
        }
        // 1. 构建请求参数
        GenerationParam param = GenerationParam.builder()
                .model(dashScopeConfig.getCustomerservice())
                .apiKey(dashScopeConfig.getApiKey())
                .messages(ref.history)
                .topP(0.8)
                .resultFormat(GenerationParam.ResultFormat.MESSAGE)
                .enableSearch(true)
                .build();
        try {
            GenerationResult result = generation.call(param);
            if (result != null && result.getOutput() != null && result.getOutput().getChoices() != null) {
                String content = result.getOutput().getChoices().get(0).getMessage().getContent();
                return content;
            }
        }catch (Exception e)
        {
            log.error("调用通义千问大模型发生异常", e);
        }
        return null;
    }

    /**
     * 从redis里面获取历史
     * @param userId
     * @return
     */
    @Nullable
    @Contract(pure = true)
    private List<Message> getRedisChatHistory(Long userId) {
        String key = CHAT_HISTORY_KEY_PREFIX + userId + ":chatHistory";
        return (List<Message>) redisTemplate.opsForValue().get(key);
    }

    /**
     * 处理结果
     * @param result
     * @param emitter
     * @param aiResponseBuilder 用于构建AI完整回复的字符串构建器
     */
    private void processResult(GenerationResult result, FluxSink<String> emitter, StringBuilder aiResponseBuilder) {
        try {
            // 解析响应结构
            String content = result.getOutput()
                    .getChoices()
                    .get(0)
                    .getMessage()
                    .getContent();

            if (content != null && !content.isEmpty()) {
                aiResponseBuilder.append(content); // 将每段回复追加到构建器中
                emitter.next(content);
            }

        } catch (Exception e) {
            emitter.error(new RuntimeException("解析响应失败: " + e.getMessage()));
        }
    }

    /**
     * 获取用户id
     * @return
     */
    @Nullable
    @Contract(pure = true)
    private Long getUserId() {
        //获取用户id
        LoginUser loginUser = LoginUserHolder.getLoginUser();
        if (loginUser == null) {
           throw new BaseException(ResultCodeEnum.TOKEN_INVALID);
        }
        return loginUser.getUserId();
    }
}
